/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : ADC MultiChannel Example
 ******************************************************************************
 * @attention
 *
 * ADC多通道采集和DMA传输示例
 * 功能：
 *   - ADC初始化
 *   - 多通道配置
 *   - DMA传输
 *   - 数据处理
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "stm32h7xx.h"
#include <stdio.h>

/* Private defines -----------------------------------------------------------*/
#define ADC_CHANNEL_COUNT   4
#define ADC_BUFFER_SIZE     ADC_CHANNEL_COUNT

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint16_t adc_buffer[ADC_BUFFER_SIZE];

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void adc_init(void);
void dma_init(void);
void usart_init(uint32_t baudrate);
void usart_send_byte(uint8_t byte);
float adc_to_voltage(uint16_t adc_value);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       USART初始化
 *
 * @param[in]   baudrate - 波特率
 *
 * @retval      无
 */
void usart_init(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    UART_HandleTypeDef huart1 = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART1_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    huart1.Instance = USART1;
    huart1.Init.BaudRate = baudrate;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
    huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

    HAL_UART_Init(&huart1);
}

/**
 * @brief       发送单字节
 *
 * @param[in]   byte - 要发送的字节
 *
 * @retval      无
 */
void usart_send_byte(uint8_t byte)
{
    while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE) == RESET);
    USART1->TDR = byte;
}

/**
 * @brief       printf重定向
 *
 * @param[in]   ch - 字符
 * @param[in]   f - 文件指针
 *
 * @retval      int - 字符
 */
int fputc(int ch, FILE *f)
{
    usart_send_byte((uint8_t)ch);
    return ch;
}

/**
 * @brief       DMA初始化
 *
 * @param       无
 *
 * @retval      无
 */
void dma_init(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();

    hdma_adc1.Instance = DMA1_Stream0;
    hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

    HAL_DMA_Init(&hdma_adc1);
    __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
}

/**
 * @brief       ADC初始化
 *
 * @param       无
 *
 * @retval      无
 */
void adc_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    ADC_ChannelConfTypeDef sConfig = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_ADC1_CLK_ENABLE();

    /* 配置ADC引脚 */
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 配置ADC */
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.NbrOfConversion = ADC_CHANNEL_COUNT;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DMAContinuousRequests = ENABLE;
    hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
    hadc1.Init.OversamplingMode = DISABLE;

    HAL_ADC_Init(&hadc1);

    /* 配置ADC通道 */
    for(int i = 0; i < ADC_CHANNEL_COUNT; i++)
    {
        sConfig.Channel = ADC_CHANNEL_0 + i;
        sConfig.Rank = ADC_REGULAR_RANK_1 + i;
        sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
        sConfig.SingleDiff = ADC_SINGLE_ENDED;
        sConfig.OffsetNumber = ADC_OFFSET_NONE;
        sConfig.Offset = 0;
        HAL_ADC_ConfigChannel(&hadc1, &sConfig);
    }

    /* 初始化DMA */
    dma_init();
}

/**
 * @brief       ADC值转换为电压
 *
 * @param[in]   adc_value - ADC采样值
 *
 * @retval      float - 电压值（V）
 */
float adc_to_voltage(uint16_t adc_value)
{
    return (adc_value / 4095.0f) * 3.3f;
}

/**
 * @brief       系统时钟配置
 *
 * @param       无
 *
 * @retval      无
 */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 5;
    RCC_OscInitStruct.PLL.PLLN = 160;
    RCC_OscInitStruct.PLL.PLLP = 2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL_RANGE_VCO_250_500M;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL_VCORANGE_WIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
}

/**
 * @brief       主函数
 *
 * @param       无
 *
 * @retval      int
 */
int main(void)
{
    /* 初始化 */
    HAL_Init();
    SystemClock_Config();
    usart_init(115200);
    adc_init();

    /* 打印欢迎信息 */
    printf("\r\n");
    printf("================================\r\n");
    printf("STM32H7xx ADC MultiChannel Example\r\n");
    printf("================================\r\n");
    printf("ADC Channels: %d\r\n", ADC_CHANNEL_COUNT);
    printf("ADC Resolution: 12-bit\r\n");
    printf("\r\n");

    /* 启动ADC DMA采集 */
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);

    /* 主循环 */
    uint32_t counter = 0;
    while(1)
    {
        /* 每500ms打印一次ADC数据 */
        if(counter % 50 == 0)
        {
            printf("ADC Data [%ld]:\r\n", counter / 50);
            printf("  CH0: %4d (%.2fV)\r\n", adc_buffer[0], adc_to_voltage(adc_buffer[0]));
            printf("  CH1: %4d (%.2fV)\r\n", adc_buffer[1], adc_to_voltage(adc_buffer[1]));
            printf("  CH2: %4d (%.2fV)\r\n", adc_buffer[2], adc_to_voltage(adc_buffer[2]));
            printf("  CH3: %4d (%.2fV)\r\n", adc_buffer[3], adc_to_voltage(adc_buffer[3]));
            printf("\r\n");
        }

        counter++;
        HAL_Delay(10);
    }

    return 0;
}

/**
 * @brief       系统异常处理
 *
 * @param       无
 *
 * @retval      无
 */
void HardFault_Handler(void)
{
    while(1)
    {
        HAL_Delay(100);
    }
}
